<?php
// +----------------------------------------------------------------------
// | INPHP
// | Copyright (c) 2023 https://inphp.cc All rights reserved.
// | Licensed ( https://opensource.org/licenses/MIT )
// | Author: 幺月儿(https://gitee.com/lulanyin) Email: inphp@qq.com
// +----------------------------------------------------------------------
// | 模块管理
// +----------------------------------------------------------------------
namespace app\admin\http\api;

use app\sso\model\UserModel;
use GuzzleHttp\Client;
use Inphp\Core\Db\Db;
use Inphp\Core\Db\Redis;
use Inphp\Core\Object\Message;
use Inphp\Core\Service;
use Inphp\Core\Util\File;
use Inphp\Core\Util\Str;

#[\app\admin\attributes\auth] class modules
{

    /**
     * 账户信息
     * @var array
     */
    public array $user = [];

    /**
     * 保存单个模块配置
     * @return Message
     */
    public function save(): Message
    {
        if ($this->user["admin"] != 1) {
            return httpMessage(31);
        }
        //自定义图标
        $icon = POST("icon");
        $icon = !empty($icon) ? trim($icon) : null;
        //自定义名称
        $name = POST("name");
        $name = !empty($name) ? Str::trim($name) : null;
        //访问路径
        $key = POST("key");
        $key = !empty($key) ? Str::trim($key) : null;
        //文件夹
        $value = POST("value");
        $value = !empty($value) ? Str::trim($value) : null;
        if (empty($value)) {
            return httpMessage("缺少模块参数");
        }
        if (preg_match("/^[a-z][a-z0-9_]*[a-z0-9]$/i", $value) <= 0) {
            return httpMessage("模块文件夹名称无效");
        }
        $key = !empty($key) ? $key : $value;
        //自定义域名
        $domain = POST("domain");
        $domain = !empty($domain) ? Str::trim($domain) : null;
        //是否启用
        $enable = POST("enable");
        $enable = $enable == 1;
        //是否是默认模块
        $default = POST("default");
        $default = $default == 1;
        //
        $newModule = [
            "enable"    => $enable,
            "icon"      => $icon,
            "name"      => $name,
            "key"       => $key,
            "value"     => $value,
            "domain"    => $domain,
            "default"   => $default
        ];
        //读取现有配置
        $modules = \Inphp\Core\Config::get("public.modules");
        $set = false;
        foreach ($modules as &$module) {
            if ($module["value"] === $value) {
                $set = true;
                $module = $newModule;
            }
        }
        if (!$set) {
            $modules[] = $newModule;
        }
        if (!$this->saveModules($modules, "key")) {
            return httpMessage("配置文件保存失败，请检测是否有写入权限！");
        }
        return httpMessage(0, "配置保存成功");
    }

    /**
     * 模块启用、禁用
     * @return Message
     */
    public function switch(): Message
    {
        if ($this->user["admin"] != 1) {
            return httpMessage(31);
        }
        //文件夹
        $value = POST("value", POST("moduleName"));
        $value = !empty($value) ? Str::trim($value) : null;
        if (empty($value)) {
            return httpMessage("缺少模块参数");
        }
        if ($value == 'admin' || $value == 'install') {
            return httpMessage("该模块禁止变更状态");
        }
        if (preg_match("/^[a-z][a-z0-9_]*[a-z0-9]$/i", $value) <= 0) {
            return httpMessage("模块文件夹名称无效");
        }
        //
        $enable = POST("enable");
        $enable = $enable == 1 || $enable == '0' ? $enable : null;
        //读取现有配置
        $modules = $this->list();
        foreach ($modules as &$module) {
            if ($module["value"] === $value) {
                $module["enable"] = $enable == 1 ? true : ($enable == '0' ? false : !$module["enable"]);
            }
        }
        if (!$this->saveModules($modules, "key")) {
            return httpMessage("配置文件保存失败，请检测是否有写入权限！");
        }
        return httpMessage(0, "配置保存成功");
    }

    /**
     * 统一保存
     * @param array $modules
     * @param string|null $key
     * @return bool
     */
    private function saveModules(array $modules = [], ?string $key = null): bool
    {
        if (empty($key)) {
            return false;
        }
        //编写PHP文件模板
        $contents = [];
        $contents[] = "<?php";
        $contents[] = "// +----------------------------------------------------------------------";
        $contents[] = "// | INPHP";
        $contents[] = "// | Copyright (c) 2023 https://inphp.cc All rights reserved.";
        $contents[] = "// | Licensed ( https://opensource.org/licenses/MIT )";
        $contents[] = "// | Author: 幺月儿(https://gitee.com/lulanyin) Email: inphp@qq.com";
        $contents[] = "// +----------------------------------------------------------------------";
        $contents[] = "// | 该文件主要用于保存自定义模块配置，可在控制面板修改保存";
        $contents[] = "// +----------------------------------------------------------------------";
        $contents[] = "return [";
        $contents[] = "    //key -> value，对应 ../modules.php 的 list 的 key -> value, domain: 可自定义域名，如果多个模块，域名不同的情况下，打开对应域名，自动进入该模块默认入口";
        $contents[] = "    //可自定义图标、名称，会自动覆盖模块的独立配置";
        foreach ($modules as $module) {
            $icon = $module["icon"] ?? null;
            $icon = !empty($icon) ? str_replace('"', "", $icon) : null;
            $name = $module["name"] ?? null;
            $name = !empty($name) ? str_replace('"', "", $name) : null;
            $domain = $module["domain"] ?? null;
            $domain = !empty($domain) ? str_replace('"', "", $domain) : null;
            $key = $module["key"] ?? null;
            $key = str_replace('"', "", $key);
            $module["enable"] = $module["value"] == "install" ? false : $module["enable"];
            $contents[] = "    [\"icon\" => \"".($icon ?? '')."\", \"name\" => \"".($name ?? '')."\", \"key\" => \"{$key}\", \"value\" => \"{$module["value"]}\", \"domain\" => \"".($domain ?? '')."\", \"enable\" => ".($module["enable"] || $module["value"] == "admin" ? "true" : "false").", \"default\" => ".($module["default"] ? "true" : "false")."],";
        }
        $contents[] = "];";
        //保存文件
        return \Inphp\Core\Config::save("public/modules.php", join("\r\n", $contents));
    }

    /**
     * 获取已安装的模块列表
     * @return array
     */
    public function list(): array
    {
        if ($this->user["admin"] != 1) {
            return [];
        }
        //直接读取所有文件夹
        $mc = \Inphp\Core\Config::get("modules");
        $apps = File::getDirs($mc["root"]);
        $list = [];
        //配置
        $modulesConfig = \Inphp\Core\Config::get("public.modules");
        foreach ($apps as $app) {
            if (preg_match("/^[a-z][a-z0-9_]*[a-z0-9]$/i", $app["name"]) > 0 && (is_file($app["path"]."/module.php") || is_file($app["path"]."/module.json"))) {
                $item = is_file($app["path"]."/module.json") ? file_get_contents($app["path"]."/module.json") : (include $app["path"]."/module.php");
                $item = is_array($item) ? $item : (@json_decode($item, true) ?? []);
                $detail = $item["detail"] ?? [];
                $detail["authors"] = $detail["authors"] ?? [];
                $detail["url"] = $detail["url"] ?? null;
                $row = [
                    "appid"     => $item["appid"] ?? null,
                    "key"       => $app["name"],
                    "id"        => $app["name"],
                    "value"     => $app["name"],
                    "icon"      => $item["icon"] ?? null,
                    "name"      => $item["name"] ?? "未命名模块",
                    "version"   => $item["version"] ?? 0,
                    "detail"    => $detail,
                    "configUrl" => $item["configUrl"] ?? null,
                    "enable"    => false,
                    "default"   => false,
                    "domain"    => ""
                ];
                foreach ($modulesConfig as $mc) {
                    if ($mc["value"] === $row["value"]) {
                        $row["key"] = !empty($mc["key"]) ? $mc["key"] : $row["key"];
                        $row["value"] = !empty($mc["value"]) ? $mc["value"] : $row["value"];
                        $row["icon"] = !empty($mc["icon"]) ? $mc["icon"] : $row["icon"];
                        $row["name"] = !empty($mc["name"]) ? $mc["name"] : $row["name"];
                        $row["domain"] = !empty($mc["domain"]) ? $mc["domain"] : $row["domain"];
                        $row["enable"] = isset($mc["enable"]) && $mc["enable"];
                        $row["default"] = isset($mc["default"]) && $mc["default"];
                    }
                }
                $list[] = $row;
            }
        }
        return $list;
    }

    /**
     * 获取所有模块控制面板菜单
     * @return array
     */
    public function menus(): array
    {
        $sso = \Inphp\Core\Modules::getModule("sso") ?? null;
        $userBelongPermissions = [];
        if ($this->user["admin"] != 1 && $sso && !empty($this->user["groups"])) {
            $userBelongGroups = Db::from("sso_group")
                ->whereIn("id", $this->user["groups"])
                ->get();
            foreach ($userBelongGroups as $group) {
                if (!empty($group["permission"])) {
                    $userBelongPermissions[] = @json_decode($group["permission"], true) ?? [];
                }
            }
        }
        //
        $moduleName = GET("moduleName", GET("module"));
        if (!empty($moduleName)) {
            return $this->menu($moduleName, $userBelongPermissions);
        }
        $list = \Inphp\Core\Modules::getList();
        $result = [];
        $index = 0;
        //取自定义配置
        $mc = \Inphp\Core\Config::get("public.modules");
        foreach ($list as $item) {
            $extUrl = $item->getConfig("extUrl", "admin");
            if (!empty($extUrl)) {
                if (stripos($extUrl, "http") !== 0) {
                    if (stripos($extUrl, ".") === 0) {
                        $extUrl = url(substr($extUrl, 1), $item->id);
                    } elseif (stripos($extUrl, "/") === 0) {
                        $extUrl = url($extUrl);
                    }
                }
            }
            //
            $menus = $item->getAdminMenus();
            if ($item->id === "admin" && $this->user["admin"] == 1) {
                //是否添加APP应用中心
                $store = \Inphp\Core\Modules::getModule("admin")->getConfig("store");
                if (($store["enable"] ?? false) && !empty($store["url"])) {
                    $menus[] = [
                        "icon"      => "fa-brands fa-app-store",
                        "name"      => "应用商店",
                        "url"       => $store["url"]."?key=".($store["key"] ?? '')
                    ];
                }
            }
            if ($this->user["admin"] != 1) {
                //非超管，需要过滤菜单
                $menus = $this->filterMenus($menus, $userBelongPermissions);
            }
            if (!empty($menus)) {
                $icon = $item->config["icon"] ?? "fa-light fa-bookmark";
                $name = $item->name;
                foreach ($mc as $m) {
                    if ($m["value"] === $item->id) {
                        $icon = !empty($m["icon"]) ? $m["icon"] : $icon;
                        $name = !empty($m["name"]) ? $m["name"] : $name;
                    }
                }
                //取菜单列表
                $result[] = [
                    "id"        => $item->id,
                    "name"      => $name,
                    "icon"      => $icon,
                    "menus"     => $menus,
                    "weight"    => $index,
                    "extUrl"    => $extUrl
                ];
            }
            $index++;
        }
        return $result;
    }

    /**
     * 获取单个模块的后台菜单
     * @param string $moduleName
     * @param array $userBelongPermissions
     * @return array
     */
    private function menu(string $moduleName, array $userBelongPermissions): array
    {
        if ($module = \Inphp\Core\Modules::getModule($moduleName)) {
            $menus = $module->getAdminMenus();
            if ($module->id === "admin" && $this->user["admin"] == 1) {
                //是否添加APP应用中心
                $store = \Inphp\Core\Modules::getModule("admin")->getConfig("store");
                if (($store["enable"] ?? false) && !empty($store["url"])) {
                    $menus[] = [
                        "icon"      => "fa-brands fa-app-store",
                        "name"      => "应用商店",
                        "url"       => $store["url"]
                    ];
                }
            }
            if ($this->user["admin"] != 1) {
                //非超管，需要过滤菜单
                $menus = $this->filterMenus($menus, $userBelongPermissions);
            }
            return $menus;
        }
        return [];
    }

    /**
     * 使用权限过滤
     * @param array $menus
     * @param array $permissionList
     * @return array
     */
    private function filterMenus(array $menus, array $permissionList): array
    {
        $list = [];
        foreach ($menus as $menu) {
            if (!empty($menu["subMenu"])) {
                $menu["subMenu"] = $this->filterMenus($menu["subMenu"], $permissionList);
                if (empty($menu["subMenu"])) {
                    unset($menu["subMenu"]);
                } else {
                    $subMenus = [];
                    foreach ($menu["subMenu"] as $sm) {
                        $sm["admin"] = $sm["admin"] ?? false;
                        if (!$sm["admin"] && (empty($sm["permission"]) || UserModel::matchPermission($menu["permission"], $permissionList))) {
                            $subMenus[] = $sm;
                        }
                    }
                    $menu["subMenu"] = $subMenus;
                }
            }
            $menu["admin"] = $menu["admin"] ?? false;
            if (!$menu["admin"] && (empty($menu["permission"]) || UserModel::matchPermission($menu["permission"], $permissionList))) {
                $list[] = $menu;
            }
        }
        return $list;
    }

    /**
     * 检测应用是否安装
     * @return Message
     */
    public function check(): Message
    {
        $appid = GET("appid");
        $appid = is_numeric($appid) && $appid > 0 ? ceil($appid) : 0;
        if ($appid <= 0) {
            return httpMessage("appid无效");
        }
        //是否在安装中
        $stateCache = Redis::get("store_appid_{$appid}");
        if (!empty($stateCache)) {
            return httpMessage([
                "appid"     => $appid,
                "state"     => 2
            ]);
        }
        //检测系统中是否存在
        $list = $this->list();
        foreach ($list as $item) {
            if ($item["appid"] == $appid) {
                Redis::del("store_appid_{$appid}");
                //已安装
                return httpMessage([
                    "appid"     => $appid,
                    "state"     => 1
                ]);
            }
        }
        return httpMessage(7000, "应用模块未安装", [
            "appid"     => $appid,
            "state"     => 0
        ]);
    }

    /**
     * 检测应用模块
     * @return Message
     */
    public function install(): Message
    {
        if ($this->user["admin"] != 1) {
            return httpMessage(31);
        }
        $appid = POST("appid");
        $appid = !empty($appid) ? Str::trim($appid) : 0;
        $appid = is_numeric($appid) && $appid > 0 ? ceil($appid) : 0;
        if ($appid <= 0) {
            return httpMessage("appid无效");
        }
        //是否在安装中
        $stateCache = Redis::get("store_appid_{$appid}");
        if (!empty($stateCache)) {
            return httpMessage([
                "appid"     => $appid,
                "state"     => 2
            ]);
        }
        //检测系统中是否存在
        $list = $this->list();
        foreach ($list as $item) {
            if ($item["appid"] == $appid) {
                //已安装
                Redis::del("store_appid_{$appid}");
                return httpMessage([
                    "appid"     => $appid,
                    "state"     => 1
                ]);
            }
        }
        //先从应用商城获取应用信息
        $store = \Inphp\Core\Modules::getModule("admin")->getConfig("store") ?? [];
        if (!$store["enable"]) {
            return httpMessage("请先在系统配置中，打开应用商店功能");
        }
        if (empty($store["url"])) {
            return httpMessage("请先在系统配置中，填写应用商店地址");
        }
        if (empty($store["key"])) {
            return httpMessage("请先在系统配置中，填写应用商店密钥");
        }
        $client = new Client();
        $response = $client->request("GET", $store["url"]."/api/app", [
            "query" => [
                "appid"     => $appid,
                "key"       => $store["key"],
                //获取发行版本
                "release"   => 1
            ]
        ]);
        if ($response->getStatusCode() !== 200) {
            return httpMessage("应用商店无响应");
        }
        $json = $response->getBody()->getContents();
        $json = !empty($json) ? (@json_decode($json, true) ?? []) : [];
        if (empty($json)) {
            return httpMessage("应用商店响应数据错误");
        }
        $json["error"] = $json["error"] ?? 1;
        $json["message"] = $json["message"] ?? null;
        $json["data"] = $json["data"] ?? null;
        if ($json["error"] !== 0 || empty($json["data"])) {
            return httpMessage($json["message"] ?? "应用商店中不存在此应用模块");
        }
        //
        $module = $json["data"];
        $id = $module["id"] ?? null;
        if (empty($id)) {
            return httpMessage(1, "应用模块ID无法判断处理", $module);
        }
        foreach ($list as $item) {
            if ($item["id"] == $id) {
                return httpMessage("系统已存在相同命名空间的应用模块");
            }
        }
        //开始执行安装，可以考虑使用swoole task异步任务处理
        $logs = \Inphp\Core\Config::get("define.logs");
        if (!is_dir($logs."/installModules")) {
            @mkdir($logs."/installModules", 0777, true);
        }
        if (!Service::isCLI()) {
            set_time_limit(0);
        }
        list ($output, $code, $cmd) = CMD_RUN("app.admin.cmd.installModule", [
            "appid"     => $appid,
            "output"    => "true"
        ], false);
        return httpMessage([
            "appid"     => $appid,
            "state"     => 2,
            "code"      => $code,
            "output"    => $output,
            "cmd"       => $cmd
        ]);
    }
}